#pragma once
#include "platform.h"
#include <string>
#include <unordered_map>
#include <string.h> // memset()


/// Key-Value store (unordered, case-insensitive, path-separator-insensitive).
template<typename T> class HashTable
{
public:
	HashTable(size_t size);
	~HashTable(void);

	T* find(const char* key);
	bool insert(const char* key, T* value);
	bool delete_(const char* key);
	bool remove(const char* key);
	size_t foreach_val(T** arr, size_t num);
	size_t count(void) const;

private:
	std::string normalize(const char* str);

	typedef std::unordered_map<std::string, T*> hashmap_t;
	hashmap_t m_table;
};


template<typename T> HashTable<T>::HashTable(size_t size)
: m_table(size)
{
}


template<typename T> HashTable<T>::~HashTable(void)
{
	for( hashmap_t::const_iterator it = m_table.begin(); it != m_table.end(); ++it )
		delete it->second;
}


template<typename T> T* HashTable<T>::find(const char* key)
{
	std::string str = normalize(key);
	hashmap_t::iterator it = m_table.find(str);
	if( it == m_table.end() )
		return NULL; // not found
	
	return it->second;
}


template<typename T> bool HashTable<T>::insert(const char* key, T* value)
{
	std::string str = normalize(key);
	hashmap_t::iterator it = m_table.find(str);
	if( it != m_table.end() )
		return false; // already present in hash table

	m_table[str] = value;
	return true;
}


template<typename T> bool HashTable<T>::delete_(const char* key)
{
	std::string str = normalize(key);
	hashmap_t::iterator it = m_table.find(str);
	if( it == m_table.end() )
		return false; // not found

	delete it->second;
	m_table.erase(it);
	return true;
}


template<typename T> bool HashTable<T>::remove(const char* key)
{
	std::string str = normalize(key);
	hashmap_t::iterator it = m_table.find(str);
	if( it == m_table.end() )
		return false; // not found

	m_table.erase(it);
	return true;
}


template<typename T> size_t HashTable<T>::foreach_val(T** arr, size_t num)
{
	int n = 0;

	// dump contents of hash table
	for( hashmap_t::const_iterator it = m_table.begin(); it != m_table.end() && n < num; ++it )
		arr[n++] = it->second;

	// zero out the rest
	memset(&arr[n], NULL, (num - n) * sizeof(T*));

	return n;
}


template<typename T> size_t HashTable<T>::count(void) const
{
	return m_table.size();
}


/// converts string to lowercase and replaces backslashes with slashes
template<typename T> std::string HashTable<T>::normalize(const char* str)
{
	std::string res = str;

	for( size_t i = 0; i < res.size(); ++i )
	{
		char& c = res[i];

		if( c >= 'A' && c <= 'Z' )
			c = TOLOWER(c);
		else
		if( c == '\\' )
			c = '/';
	}

	return res;
}
